// 两类数据类型子集：标量（scalar）和复合（compound）

#[cfg(test)]
mod tests {
    #[test]
    fn scalar() {
        // 标量（scalar）类型代表一个单独的值
        // 整型,整型溢出
        // 当在 debug 模式编译时，Rust 检查这类问题并使程序 panic，这个术语被 Rust 用来表明程序因错误而退出。
        // 在 release 构建中，Rust 不检测溢出，相反会进行一种被称为 「two’s complement wrapping」 的操作。简而言之，256 变成 0，257 变成 1，依此类推。依赖溢出被认为是一种错误，即便可能出现这种行为。
        // 如果你确实需要这种行为，标准库中有一个类型显式提供此功能，Wrapping。

        // 浮点型
        let x = 2.0; // f64
        let y: f32 = 3.0; // f32
        println!("{:?}:{:?}", x, y);

        let base: u8 = 255; // f64
        // u8 ，它可以存放从零到 255 的值。那么当你将其修改为 256 「integer overflow
        // println!("base:{:?},max:{:?}", base, base + 1); //err 溢出panic

        // bool
        let t = true;

        // Rust 的 char 类型是语言中最原生的字母类型
        let c = 'z';
        let z = 'ℤ';
        // Rust 的 char 类型代表了一个 Unicode 标量值（Unicode Scalar Value）
        let heart_eyed_cat = '?';
    }

    #[test]
    fn compound() {
        // 元组类型
        let tup: (i32, f64, u8) = (500, 6.4, 1);
        // 可以使用模式匹配（pattern matching）来解构（destructure）元组值
        let (x, y, z) = tup;
        println!("base:{:?},max:{:?}", tup.0, x,); //err 溢出panic

        // 数组
        //数组是一整块分配在栈上的内存。可以使用索引来访问数组的元素
        let months = ["January", "February", "March", "April", "May"];
    }
}

#[cfg(test)]
mod str_test {
    #[test]
    fn char() {
        let months = [
            //Unicode 4字節
            'z', 'ℤ', '国', '😻',
        ];
        for x in months {
            print!("{}:{}, ", x, std::mem::size_of_val(&x));
        }
        println!();

        let world = &months[0..2];
        println!("size:{:?}", world);
        // println!("size:{:?}", months[0..2]); //ERR
    }

    // Rust 中的字符是 Unicode 类型，因此每个字符占据 4 个字节内存空间，
    // 字符串中不一样，字符串是 UTF-8 编码，也就是字符串中的字符所占的字节数是变化的(1 - 4)
    // 字符串的底层的数据存储格式实际上是[ u8 ]，一个字节数组,切片的索引是通过字节来进行
    #[test]
    fn str() {
        let chinese = "中国人"; //中文在 UTF-8 中占用三个字节
        println!("{:?}:{:?}", chinese, std::mem::size_of_val(chinese)); // 9
        //切片->字节拆分 ，want 按照字符拆分
        println!(
            "{:?}:{:?}:{:?}",
            "中国人[0..3}",
            &chinese[0..3],
            std::mem::size_of_val(&chinese[0..3])
        );

        let hello = String::from("hello");
        println!(
            "{:?}:{:?}:{:?}",
            "hello[0..3}",
            &hello[0..3],
            std::mem::size_of_val(&hello[0..3])
        );
        // greet(my_name);
        // hello[0] //err
        // word[0]; // 字节索引

        let word = first_word(&hello);
        // hello.clear(); // error! clear 需要清空改变 String，因此它需要一个可变借用
        println!("the first word is: {}", word); //println! 又使用了不可变借用
    }

    fn first_word(s: &String) -> &str {
        &s[..1]
    }

    #[test]
    fn str_method() {
        //中文在 UTF-8 中占用三个字节
        let mut chinese = String::from("中国人");
        println!("{:?}:{:?}", chinese, std::mem::size_of_val(&chinese)); // 3 * 8
        chinese.push('a');
        println!("{:?}:{:?}", chinese, std::mem::size_of_val(&chinese));
        chinese.push('b');
        chinese.push('c');
        println!("{:?}:{:?}", chinese, std::mem::size_of_val(&chinese));

        // chinese.insert(2, 's'); err
        chinese.insert(3, 's');
        println!("{:?}:{:?}", chinese, std::mem::size_of_val(&chinese));

        let mut new_str = chinese.replace("ab", "abc");
        println!("{:?}:{:?}", new_str, std::mem::size_of_val(&new_str));

        new_str.replace_range(10..13, "R");
        println!("{:?}:{:?}", new_str, std::mem::size_of_val(&new_str));
        // dbg!(new_str);
        let del = new_str.remove(11);
        println!("{:?}:{:?}", del, new_str);
        new_str.truncate(3);
        println!("truncate[start...]:{:?}", new_str);
        new_str.clear();
        println!("clear == truncate[0..] ");
    }

    #[test]
    fn str_concatenate() {
        let string_append = String::from("hello ");
        let string_rust = String::from("rust");
        // &string_rust会自动解引用为&str
        let result = string_append + &string_rust;
        let mut result = result + "!"; // `result + "!"` 中的 `result` 是不可变的
        result += "!!!";
        println!("result {}", result);

        let new_result = format!("{} {}!", result, "aa");
        println!("result {}", new_result);

        //
    }

    //转义的方式 \ 输出 ASCII 和 Unicode 字符
    #[test]
    fn str_s() {
        let byte_escape = ":\x52\x75\x73\x74!"; //转义

        println!("\x3F (\\x3F means ?) {}", byte_escape);
        // \u 可以输出一个 unicode 字符
        let unicode_codepoint = "\u{211D}";
        let character_name = "\"DOUBLE-STRUCK CAPITAL R\"";

        println!(
            "Unicode character {} (U+211D) is called {}",
            unicode_codepoint, character_name
        );
        println!("{}", "hello \\x52\\x75\\x73\\x74");
        // 换行了也会保持之前的字符串格式
        // 使用\忽略换行符
        let long_string = "String literals
                        The linebreak and indentation here ->\
                        <- can be escaped too!";
        println!("{}", long_string);
    }

    #[test]
    fn str_op() {
        for c in "中国人".chars() {
            println!("{}", c);
        }
        for b in "中国人".bytes() {
            println!("{}", b);
        }
    }
}
